﻿#include "lua_rpc.h"

#include "asio/asio_server.h"  // for DisconnectClient()
#include "get_world.h"  // for GetWorld()
#include "log.h"
#include "rpc/rpc_call_context.h"  // for CRpcCallContext
#include "rpc/rpc_helper.h"  // for RpcHelper
#include "world.h"  // for GetLuaState()

#include <LuaIntf/LuaIntf.h>

const char LOG_NAME[] = "LuaRpc";

namespace {

void OnResponse(uint64_t uCltId, uint32_t uLuaRpcId,
	const std::string& sRespContent)
{
	using LuaIntf::LuaRef;
	LuaRef require(GetWorld()->GetLuaState(), "require");
	try {
		LuaRef handler = require.call<LuaRef>("rpc_response_handler");
		handler.dispatchStatic("handle", uCltId, uLuaRpcId, sRespContent);
	}
	catch (const LuaIntf::LuaException& e) {
		LOG_ERROR("Failed to call lua rpc_response_handler.handle(), " << e.what());
	}
}

}  // namespace

namespace {

void ReplyTo(const CRpcCallContext& ctx, const std::string& sResponse)
{
	// Todo: check CRpcCallContext?
	RpcHelper::ReplyTo(ctx, sResponse);
}

RpcCallback ToRpcCallback(const LuaIntf::LuaRef& luaCallback)
{
	// Default is empty callback.
	if (!luaCallback)
		return RpcCallback();  // skip nil
	if (luaCallback.isFunction())
		return luaCallback.toValue<RpcCallback>();  // Todo: catch
	LOG_WARN("Lua callback in request is " << luaCallback.typeName());
	return RpcCallback();
}

void Request(uint64_t uCltId, const std::string& sService,
	const std::string& sMethod, const std::string& sRequest,
	const LuaIntf::LuaRef& luaCallback)
{
	RpcCallback cb = ToRpcCallback(luaCallback);
	RpcHelper::Request(uCltId, sService, sMethod, sRequest, cb);
}

void RequestByBase(const LuaIntf::LuaRef& luaGameCltId,
	const std::string& sService, const std::string& sMethod,
	const std::string& sRequest, const LuaIntf::LuaRef& luaCallback)
{
	// Todo: Check parameters...
	// Todo: try/catch?
	CGameCltId gameCltId = luaGameCltId.toValue<CGameCltId>();
	RpcCallback cb = ToRpcCallback(luaCallback);
	RpcHelper::Request(gameCltId, sService, sMethod, sRequest, cb);
}

// 跨服请求， uSvrId 为服务器ID
void RequestSvr(uint16_t uSvrId, const std::string& sService,
	const std::string& sMethod, const std::string& sRequest,
	const LuaIntf::LuaRef& luaCallback)
{
	RpcCallback cb = ToRpcCallback(luaCallback);
	RpcHelper::RequestSvr(uSvrId, sService, sMethod, sRequest, cb);
}

// 服务器主动关闭当前的客户端连接
void DisconnectClient(uint64_t uCltId)
{
	GetWorld()->GetServer()->DisconnectClient(uCltId);
}

}  // namespace

namespace LuaRpc {

void Bind(lua_State* L)
{
	assert(L);
	// 所有导出模块带前缀"c_"
	LuaIntf::LuaBinding(L).beginModule("c_rpc")
		.addFunction("reply_to", &ReplyTo)
		.addFunction("request", &Request)
		.addFunction("request_by_base", &RequestByBase)
		.addFunction("request_svr", &RequestSvr)
		.addFunction("disconnect", &DisconnectClient)
	.endModule();
}

}  // namespace LuaRpc
